package cn.good.yan.controller;

import cn.good.yan.service.FlowLimitService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * 演示：服务限流 ， 需要启动 nacos、cloud-sentinel-service、sentinel的客户端服务
 * 启动成功：访问sentinel客户端页面：http://localhost:8080/ ，账号密码：sentinel
 *
 * @author shengren.yan
 * @create 2024-05-20
 */
@RestController
public class Flow1Controller {

    // 演示1：流控规则 （菜单） 配置 /testA，阈值类型：QPS 、单机阈值：2 、 流控模式：直连
    // 当我们 请求接口：http://localhost:8401/testA  ， 一次不会出现异常，多次连续点击2下，会触发服务降级
    @GetMapping("/testA")
    public String testA() {
        return "------testA";
    }

    // 演示2：流控规则 （菜单） 配置 /testA，阈值类型：QPS 、单机阈值：1 、 流控模式：关联 - 关联资源: /testB
    // 当我们 请求接口：http://localhost:8401/testA  ，正常
    // 当我们 请求接口：http://localhost:8401/testB  ，正常
    // 当我们 大量的请求：http://localhost:8401/testB 接口 的时候，再去访问 http://localhost:8401/testA ，此时的testA会进入服务降级
    // 也就是 testB 接口异常，会导致testA 接口的正常访问
    @GetMapping("/testB")
    public String testB() {
        return "------testB";
    }


    @Resource
    private FlowLimitService flowLimitService;

    /**
     * 演示3 - 流控-链路演  流控规则 （菜单） 流控模式：链路  -- 与testD绑定
     * 访问：http://localhost:8401/testC  - 单独访问 正常
     * 访问：http://localhost:8401/testD  - 单独访问 正常
     * 配置：流控 - 资源名与@SentinelResource(value = "common") 里的保持一致，阈值类型：QPS 、单机阈值：1 、 流控模式：链路  /testD
     * 多次访问：http://localhost:8401/testC  - 单独访问 正常，没有流控限制
     * 多次访问：http://localhost:8401/testD  - 超过阈值，会进入流控限制
     */
    @GetMapping("/testC")
    public String testC() {
        flowLimitService.common();
        return "------testC";
    }

    @GetMapping("/testD")
    public String testD() {
        flowLimitService.common();
        return "------testD";
    }

    /**
     * 演示4 - 流控效果 - 预热（WarmUp）
     * 选择这个，需要设置预热时长，默认是3，它是经过一段时间才逐渐升至设定的 QPS 阈值
     * 例子:阀值为10+预热时长设置5秒，它就是10/3 约等于3，即阀值刚开始为3；然后过了5秒后阀值才慢慢升高恢复到10
     * 使用场景：高并发访问，秒杀功能
     */
    @GetMapping("/testE")
    public String testE() {
        return "------testE";
    }

    /**
     * 演示5 - 流控效果 - 排队等待
     * 匀速排队：设置排队等待必须在阀值类型是QPS模式下，设置排队等待，可设置超时时间，单位毫秒。
     */
    @GetMapping("/testF")
    public String testF() {
        return "------testF";
    }

    /**
     * 演示6 - 阈值类型 - QPS、线程数
     */
    @GetMapping("/testG")
    public String testG() {
        return "------testG";
    }

    // ------ 熔断 -----------------

    /**
     * 演示7 - 熔断 - 熔断策略 ：慢调用比例
     * RT（平均响应时间，秒级）
     * 平均响应时间 超出阈值 且 在时间窗口内通过的请求>=5 ，两个条件同时满足后触发降级
     * 需要配置熔断 - RT： 200  比例阈值：0.1 熔断时长：5 秒  最小请求数： 5, 统计时长：1000ms
     */
    @GetMapping("/testH")
    public String testH() {
        try {
            // 睡眠1分钟  -  长时间睡眠，满足RT 响应时间大于 200秒， 当请求数满足 5个 ，在统计时长的范围内，开始统计，
            // 第6个请求会触发服务熔断，等 5秒后，尝试恢复，恢复成功，正常访问，失败就继续熔断
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "------testH";
    }

    /**
     * 演示8 - 熔断 - 熔断策略 ：异常比例
     * QPS >= 5 且 异常比例（秒级统计）超过阈值时，触发降级；
     * 时间窗口结束后，关闭降级，就是该接口出现异常，大于设置的异常比例，
     * 如：0.2，就是百分之20，并且QPS请求>=5，才会触发熔断
     */
    @GetMapping("/testI")
    public String testI() {
        int age = 10 / 0; // 手动制造出异常
        return "------testI";
    }

    /**
     * 演示9 - 熔断 - 熔断策略 ：异常数
     * 异常数（分钟统计）超过阈值时，触发降级；时间窗口结束后，关闭降级
     */
    @GetMapping("/testJ")
    public String testJ() {
        int age = 10 / 0; // 手动制造出异常
        return "------testJ";
    }

}
